#include "AlsaAudioSink.h"
#include "Log.h"

AlsaAudioSink::AlsaAudioSink()
{
	m_Handle = NULL;
    m_Params = NULL;
    m_Frames = 0;
}

AlsaAudioSink::~AlsaAudioSink()
{
	if(m_Handle) {
    	snd_pcm_drain(m_Handle);
    	snd_pcm_close(m_Handle);
	}
}

int AlsaAudioSink::init(snd_pcm_format_t pcm_format, unsigned int sample_rate, unsigned int channels, int frames)
{
	LOGI("alsa version: %s", SND_LIB_VERSION_STR);
	int err;
	err = snd_pcm_open(&m_Handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (err < 0) {
        LOGE("snd_pcm_open error: %s", snd_strerror(err));
        return -1;
    }
    /* 分配一个硬件参数结构体 */
    snd_pcm_hw_params_alloca(&m_Params);

    /* 使用默认参数 */
    err = snd_pcm_hw_params_any(m_Handle, m_Params);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_any error: %s", snd_strerror(err));
        return -1;
    }

    /* 设置硬件参数 */

    /* Interleaved mode */
    err = snd_pcm_hw_params_set_access(m_Handle, m_Params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_set_access error: %s", snd_strerror(err));
        return -1;
    }

    /* 数据格式*/
    err = snd_pcm_hw_params_set_format(m_Handle, m_Params, pcm_format);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_set_format error: %s", snd_strerror(err));
        return -1;
    }

    /* 声道 */
    err = snd_pcm_hw_params_set_channels(m_Handle, m_Params, channels);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_set_channels: %s", snd_strerror(err));
        return -1;
    }

    /* 设置采样率 */
    int dir = 0;
    m_SampleRate = sample_rate;
    err = snd_pcm_hw_params_set_rate_near(m_Handle, m_Params, &sample_rate, &dir);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_set_rate_near: %s", snd_strerror(err));
        return -1;
    }

    //每一帧表示采样一个点，双通道一帧等于两个采样点，以此类推
    m_Frames = frames;
    if(m_Frames == 0)
    	m_Frames = 2048;
    dir = 0;
    err = snd_pcm_hw_params_set_period_size_near(m_Handle, m_Params, &m_Frames, &dir);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_set_period_size_near error: %s", snd_strerror(err));
        return -1;
    }

    //以帧为单位
    snd_pcm_uframes_t buffer_size = m_Frames * 5;
    LOGD("buffer_size:%d", buffer_size);
	snd_pcm_hw_params_set_buffer_size_near(m_Handle, m_Params, &buffer_size);
    if (err < 0) {
        LOGE("snd_pcm_hw_params_set_buffer_size_near error: %s", snd_strerror(err));
        return -1;
    }

    LOGI("format:%d sample_rate:%d channels:%d buffer info, period:%d frames, buffer size:%d frames", \
    	pcm_format, sample_rate, channels, (int)m_Frames, (int)buffer_size);

    /* 把前面设置好的参数写入到playback设备 */
    err = snd_pcm_hw_params(m_Handle, m_Params);
    if (err < 0) {
        LOGE("snd_pcm_hw_params error: %s", snd_strerror(err));
        return -1;
    }

    /* 得到一个周期的时间长度 */
    unsigned int period_time, buffer_time;
    snd_pcm_hw_params_get_period_time(m_Params, &period_time, &dir);

    /* 得到整个buffer的时间长度 */
    snd_pcm_hw_params_get_buffer_time(m_Params, &buffer_time, &dir);

    LOGI("period time:%ums, buffer time:%ums", period_time/1000, buffer_time/1000);

    return 0;
}

double AlsaAudioSink::getBufferDuration()
{
    snd_pcm_sframes_t delay = 0;
    snd_pcm_sframes_t availp, delayp;
    snd_pcm_delay(m_Handle, &delay);
    // availp:设置的缓存区中有多少frame, delayp=delay:设置的缓存区加上驱动中的buffer大小
    // snd_pcm_avail_delay(m_Handle, &availp, &delayp);
    //LOGD("delay:%d availp:%d delayp:%d", delay, availp, delayp);
    double duration = (double)delay / (double)m_SampleRate;
    return duration;
}

int AlsaAudioSink::playFrame(uint8_t *data, int size)
{
	if(!m_Handle || !data || size == 0) {
		LOGE("data error, m_Handle(%p) data(%p) size(%d)!\n", m_Handle, data, size);
		return -1;
	}
retry:
	//默认阻塞模式
    int ret = snd_pcm_writei(m_Handle, data, size);
    if (ret == -EPIPE) {
        /* EPIPE means underrun */
        static int underrun_cnt = 0;
        LOGW("underrun occurred %d", ++underrun_cnt);
        snd_pcm_prepare(m_Handle);
    } else if (ret < 0) {
        LOGE("error from writei: %s", snd_strerror(ret));
    }  else if (ret != (int)size) {
        LOGW("short write, write %d frames", ret);
        data = data + ret*4; // 双声道16S，一个frame4byte
        goto retry;
    }
    return 0;
}